<script>
import {debounce, transformSubmitValue} from "@/utils/tools";
import Pagination from "@/components/Pagination"; // 基础分页组件 el-pagination
import TableConfig from "@/components/QueryTable/TableConfig"; // 基础分页组件 el-pagination
import defaultEmptyImg from "@/assets/empty_images/table_empty_default.png"
import searchEmptyImg from "@/assets/empty_images/table_empty_search.png"
import elementResizeDetectorMaker from 'element-resize-detector'
import dayjs from "dayjs";
// import {mergeOptions} from "@/components/QueryTable/utils";
import {mergeOptions} from "element-ui/packages/table/src/util";

const BREAKPOINTS = [
  [661, 1, 'top'],
  [991, 2, 'top'],
  [1321, 3, 'right'],
  [1981, 4, 'right'],
  [2381, 6, 'right'],
  [Infinity, 8, 'right'],
];

export default {
  name: "QueryTable",
  components: {Pagination, TableConfig},
  props: {
    layout: {
      type: Object,
      required: false,
      default: () => ({
        padding: 24,
        gutter: 24
      })
    },
    outHeight: {
      type: Number,
      default: 100
    },
    model: {
      type: Object,
      required: true,
      default: () => ({})
    },
    rules: {
      type: Object,
      default: () => ({})
    },
    gutter: {
      type: Number,
      default: 16
    },
    labelWidth: {
      type: Number | String,
      default: 90
    },
    /**
     * 如果为 false,会原样保存。
     *
     * @default true
     * @param 要不要值中的 Null 和 undefined
     */
    omitNil: {
      type: Boolean,
      default: true
    },
    transformQuery: {
      type: Function,
      default: undefined,
      validator: function (value) {
        // 必须是一个方法
        return typeof value === 'function'
      }
    },
    /**
     * @name 表单结束后调用
     * @description 支持异步操作
     *
     * @example request={async (values) => { await save(values); return true }}
     */
    request: {
      type: Function,
      default: () => ({}),
      required: true,
      validator: function (value) {
        // 这个值必须是一个方法
        return typeof value === 'function'
      }
    },
    columns: {
      type: Array,
      required: true,
      default: () => []
    },
    minTableHeight: {
      type: Number | String,
      default: 180
    },
    data: {
      type: Array,
      default: () => ([])
    },
    toolbarRender: {
      type: Function,
      default: undefined,
      validator: function (value) {
        // 必须是一个方法
        return typeof value === 'function'
      }
    },
    titleRender: {
      type: Function,
      default: undefined,
      validator: function (value) {
        // 必须是一个方法
        return typeof value === 'function'
      }
    },
    queryLoading: {
      type: Boolean,
      default: true
    },
    tableLoading: {
      type: Boolean,
      default: true
    },
    page: {
      type: Boolean,
      default: true
    },
    startPage: {
      type: Number,
      default: 1
    },
    pageSizes: {
      type: Array,
      default() {
        return [10, 20, 50, 100]
      }
    },
    batchConfig: {
      type: Object,
      required: false
    },
  },
  data() {
    return {
      spanConfig: {span: 6, layout: 'right'},
      collapse: false,
      hideCollapse: false,
      hideIndex: 0,
      qyLoading: false,
      submitterOffset: this.$slots?.default?.length || 0,
      queryModel: this.model,
      queryParams: {},
      tableData: [],
      tableSize: '',
      calcTableHeight: 0,
      tbLoading: false,
      searchMode: false,
      pageParams: {
        pageNum: this.startPage,
        pageSize: 10
      },
      pageTotal: 0,
      allSelectionObj: {},
      batchLoading: false,
      batchConfigDefault:{
        cancelText: '取消选择',
        okText: '批量删除',
        onOkCallback:()=>{}
      }
    }
  },
  computed: {
    queryFormShow() {
      return !!this.$slots.form
    },
    toolbarShow() {
      return this.toolbarRender || this.titleRender
    },
    layoutObj() {
      return Object.assign({}, {padding: 24, gutter: 24}, this.layout)
    },
    emptyObj() {
      return this.searchMode ? {emptyImg: searchEmptyImg, emptyText: '没有找到相关数据'} : {
        emptyImg: defaultEmptyImg,
        emptyText: '暂无数据'
      }
    },
    allSelections(){
      let temp = []
      for (const argumentsKey in this.allSelectionObj) {
        if(this.allSelectionObj[argumentsKey] && this.allSelectionObj[argumentsKey].length){
          temp.push(...this.allSelectionObj[argumentsKey])
        }
      }
      return temp
    }
  },
  created() {
    this.batchConfigDefault = mergeOptions(this.batchConfigDefault, this.batchConfig);
  },
  mounted() {

    this.$nextTick(() => {
      this.initTableData()
    })
    this.columns.forEach(async (item) => {
      if (item.request) {
        item._columnRequestOptions = await item.request()
      }
    })

    const erd = elementResizeDetectorMaker();

    erd.listenTo(this.$el, debounce(160, (element) => {
      this.width = element.offsetWidth;
      this.onResize()
    }));

    this.$once("hook:beforeDestroy", () => {
      erd && erd.uninstall(this.$el)
    })
  },
  methods: {
    initTableData() {
      this.submitQuery(false)
    },
    submitQuery(isSearch=true) {
      this.pageParams.current = this.startPage
      this.searchMode = isSearch
      this.queryParams = transformSubmitValue(this.queryModel, this.omitNil)
      this.$emit('search-submit', this.queryParams)
      this.getTableData()
    },
    resetQuery() {
      this.pageParams.current = this.startPage
      this.searchMode = false
      this.$refs.queryFormRef?.resetFields();
      this.queryParams = transformSubmitValue(this.queryModel, this.omitNil)
      this.$emit('search-reset', this.queryParams)
      this.getTableData(true)
    },
    onPageChange() {
      this.getTableData(true)
    },
    changeCollapse() {
      this.collapse = !this.collapse
      this.$emit('on-collapse')
      this.onResize()
    },
    onResize() {
      if (!this.queryFormShow) {
        this.updateTableHeight()
        return
      }
      // 16 = 2 * (ant-row -8px margin)
      if (!this.width) return;

      const breakPoint = BREAKPOINTS.find(item => this.width < item[0]);
      const span = 24 / breakPoint[1];
      this.spanConfig = {
        span: span,
        layout: breakPoint[2],
      };

      if (this.collapse) {
        this.hideIndex = breakPoint[1] - 1 || 1
        this.submitterOffset = 0
      } else {
        this.hideIndex = this.$slots.form.length + 1
        this.submitterOffset = 24 - ((this.spanConfig.span * this.hideIndex) % 24 || 24)
      }

      if (this.$slots.form.length < breakPoint[1]) {
        this.hideCollapse = true
        this.submitterOffset = 24 - ((this.spanConfig.span * this.hideIndex) % 24 || 24)
      } else {
        this.hideCollapse = false
      }

      this.updateTableHeight()
    },
    updateTableHeight() {
      this.$nextTick(() => {
        const windowHeight = document.documentElement.clientHeight
        const formHeight = this.queryFormShow ? this.$refs.formContainerRef?.clientHeight + this.layoutObj.padding : 0
        const toolbarHeight = this.toolbarShow ? 60 : 0
        const paginationHeight = this.page ? 48 : 0

        const calcHeight = windowHeight - this.outHeight - formHeight - paginationHeight - toolbarHeight - this.layoutObj.padding * 2 - 5

        this.calcTableHeight = Math.max(this.minTableHeight, calcHeight)
      })
    },
    refreshTable() {
      this.getTableData(true)
    },
    tableSizeChange(size) {
      this.tableSize = size
      this.$nextTick(()=>{
        this.$refs.queryTableRef?.doLayout()
      })
    },
    tableColumnChange(cols) {
      // this.tableSize = size
      // this.$nextTick(()=>{
      //   this.$refs.queryTableRef?.doLayout()
      // })
    },
    async getTableData(hideQueryLoading = false) {
      // 没设置 onFinish 就不执行
      if (!this.request) return;

      // 防止重复提交
      if (this.qyLoading) return;

      if (!hideQueryLoading) {
        this.qyLoading = true
      }

      this.tbLoading = true
      try {
        if (this.request && typeof this.request === 'function') {
          let finalQueryParams = this.queryParams;
          if (this.transformQuery && typeof this.transformQuery === 'function') {
            finalQueryParams = this.transformQuery(this.queryParams)
          }
          const {success, data, total} = await this.request(Object.assign({}, finalQueryParams, this.pageParams));
          if (success) {
            this.tableData = data || []
            this.pageTotal = parseInt(total) || 0

            // table滚动到行头
            this.$nextTick(() => {
              if(this.$refs.queryTableRef?.bodyWrapper?.scrollTop) this.$refs.queryTableRef.bodyWrapper.scrollTop = 0;
            });
          } else {
            this.tableData = []
            this.pageTotal = 0
          }
        }
        this.qyLoading = false;
        this.tbLoading = false;
      } catch (error) {
        this.qyLoading = false;
        this.tbLoading = false;
        this.tableData = []
        this.pageTotal = 0
      }
    },
    formatNumber(num) {
      num = num.toFixed(2);
      num += '';
      const x = num.split('.');
      let x1 = x[0];
      const x2 = x.length > 1 ? '.' + x[1] : '';
      const rgx = /(\d+)(\d{3})/;
      while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
      }
      return x1 + x2;
    }
  },
  render(createElement) {
    let queryFormNode = null
    let toolBarNode = null
    let batchBarNode = null
    let paginationNode = null

    const submitter = (
        <el-col span={this.spanConfig.span} offset={this.submitterOffset} style={{textAlign: 'right'}}>
          <el-form-item label-width="1">
            <template slot="label">
              <span style={{opacity: 0}}>lb</span>
            </template>
            <el-button type="default" size="small" vOn:click={this.resetQuery}>重置</el-button>
            <el-button type="primary" size="small" loading={this.queryLoading && this.qyLoading}
                       vOn:click={this.submitQuery}>查询
            </el-button>
            {
              this.hideCollapse ? null : <el-button type="text" vOn:click={this.changeCollapse}>
                {this.collapse ? '展开' : '收起'}
                <i class={this.collapse ? 'el-icon-arrow-down' : 'el-icon-arrow-up'}></i>
              </el-button>
            }

          </el-form-item>
        </el-col>
    )

    if (this.queryFormShow) {
      const splitCount = 24 / this.spanConfig.span

      queryFormNode = (
          <div
              ref="formContainerRef"
              style={{
                padding: `${this.layoutObj.padding}px ${this.layoutObj.padding}px 0 ${this.layoutObj.padding}px`,
                marginBottom: `${this.layoutObj.gutter}px`
              }}
              class="query-container-form">
            <el-form
                ref="queryFormRef"
                props={{
                  model: this.queryModel,
                  rules: this.rules,
                  size: 'small'
                }}
                labelPosition={this.spanConfig.layout}
                labelWidth={this.labelWidth + 'px'}
                labelSuffix=":"
            >
              <el-row gutter={this.gutter}>

                {
                  (this.$slots.form || []).map((colNode, index) => {
                    return (
                        <el-col span={this.spanConfig.span} vShow={index < this.hideIndex}>
                          {colNode}
                        </el-col>
                    )
                  })}
                {submitter}
              </el-row>
            </el-form>
          </div>
      )
    }

    if (this.toolbarShow) {
      toolBarNode = (
          <div class="query-table-toolbar">
            {
              this.titleRender ? <div class="query-table-toolbar-title">{this.titleRender()}</div> : null
            }
            {
              this.toolbarRender ? <div class="query-table-toolbar-operator"
                                        style={{textAlign: 'right'}}>{this.toolbarRender(this.queryParams)}</div> : null
            }
            {
              // <table-config vOn:refresh={this.refreshTable} vOn:sizeChange={this.tableSizeChange}  vOn:columnChange={this.tableColumnChange} column={this.columns}></table-config>
            }
          </div>
      )
    }

    if(this.allSelections.length){
      batchBarNode =(
          <div class="qct-batch-bar"
               style={{
                 width: `calc(100% - ${parseFloat(this.layoutObj.padding) * 2}px)`,
                 left: `${this.layoutObj.padding}px`,
                 top: `${this.layoutObj.padding}px`}}
          >
            <div class="qct-batch-bar-content"
                 style={{padding: `0 ${this.layoutObj.padding}px`}}>
              <span style={{marginRight: `${this.layoutObj.padding}px`}}>已选择 {this.allSelections.length} 项</span>
              <el-button type="text" vOn:click={() => this.clearSelection(false)}>{this.batchConfigDefault.cancelText}</el-button>
              <el-button type="text" loading={this.batchLoading} vOn:click={() => {
                this.batchLoading = true
                this.batchConfigDefault.onOkCallback(this.allSelections.slice(0)).then(result => {
                  if(result){
                    this.clearSelection(true)
                  }
                }).finally(()=>{
                  this.batchLoading = false
                })
              }}>{this.batchConfigDefault.okText}</el-button>
            </div>
          </div>
      )
    }

    if (this.page) {
      paginationNode = (
          <pagination
              total={this.pageTotal}
              page={this.pageParams.current}
              limit={this.pageParams.pageSize}
              pageSizes={this.pageSizes}
              {...{
                on: {
                  'update:page': (val) => this.pageParams.current = val,
                  'update:limit': (val) => this.pageParams.pageSize = val,
                  'pagination': this.onPageChange
                }
              }}
          >
          </pagination>
      )
    }

    const getRender = (scoped, column) => {
      let defaultRender = scoped.row[column.prop]

      if (['select'].some(item => item === column.type)) {
        if (column.valueEnum) {
          if (!!column.valueEnum[scoped.row[column.prop]]) {
            defaultRender = column.valueEnum[scoped.row[column.prop]].text
          }
        } else if (column.options) {
          const find = column.options.find(item => item.value == defaultRender)
          if (!!find) {
            defaultRender = find.label
          }
        } else if (column.request) {
          const find = column._columnRequestOptions.find(item => item.value == defaultRender)
          if (!!find) {
            defaultRender = find.label
          }
        }
      } else if (['date', 'datetime', 'time'].some(item => item === column.type)) {
        if (column.dateFormat && !!defaultRender) {
          const dayjsData = dayjs(defaultRender)
          if (dayjsData.isValid() && dayjsData.isAfter(dayjs('1900'), 'year')) {
            defaultRender = dayjs(defaultRender).format(column.dateFormat)
          }else{
            // defaultRender = customFormat(defaultRender,column.dateFormat)
          }
        }
      } else if (['money'].some(item => item === column.type)) {
        if (!isNaN(parseFloat(defaultRender))) {
          defaultRender = this.formatNumber(defaultRender)
        }
      }

      if (column.render && typeof column.render === 'function') {
        defaultRender = column.render(defaultRender, scoped.row,{scoped: scoped, pageParams: this.pageParams})
      }
      return defaultRender
    }

    const tableColumns = this.columns.map(column => {
      let scopedSlots = null
      if ('selection' === column.type){
        scopedSlots = null
      } else if('index' === column.type) {
        if(column.increment){
          scopedSlots ={
            default: (scoped) => {
              return scoped.$index + 1 + (this.pageParams.current - this.startPage) * this.pageParams.pageSize
            },
          }
        }
      }else{
        scopedSlots ={
          default: (scoped) => {
            return getRender(scoped, column)
          },
        }
      }
        return (
          <el-table-column
              props={{...column}}
              scopedSlots={scopedSlots}>
          </el-table-column>
      )
    })

    return (
        <div class="query-container">
          {
            queryFormNode
          }
          <div
              style={{padding: `${this.layoutObj.padding}px`}}
              class="query-container-table">
            {
              toolBarNode
            }
            {
              batchBarNode
            }
            <el-table
                ref="queryTableRef"
                vLoading={this.tableLoading && this.tbLoading}
                data={this.tableData}
                height={this.calcTableHeight}
                size={this.tableSize}
                headerCellStyle={() => {
                  return {backgroundColor: '#FAFAFA'}
                }}
                scopedSlots={{
                  empty: () => {
                    return <el-empty
                        description={this.emptyObj.emptyText}
                        image={this.emptyObj.emptyImg}
                    ></el-empty>
                  }
                }}
                {...{props: this.$attrs}}
                {...{on: this.$listeners}}
            >
              {tableColumns}
            </el-table>
            {paginationNode}
          </div>
        </div>
    )
  }
}
</script>

<style lang="scss" scoped>
.query-container {
  width: 100%;
  min-height: 100%;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;

  &-form {
    //padding: 24px 24px 0 24px;
    //margin-bottom: 24px;
    background: #FFFFFF;
  }

  &-table {
    //padding: 24px;
    flex: 1;
    background: white;
    position: relative;

    & .qct-batch-bar{
      position: absolute;
      box-sizing: border-box;

      &-content{
        width: 100%;
        box-sizing: border-box;
        background-color: #e6f7ff;
        border: 1px solid #91d5ff;
        display: flex;
        align-items: center;
        justify-content: flex-start;

        & > *:last-child {
          margin-left: auto;
        }
      }
    }
  }

  .query-table-toolbar {
    margin-bottom: 16px;
    line-height: 44px;
    overflow: inherit;
    display: flex;
    align-items: center;
    justify-content: space-between;

    &-title {
      flex: 0 1 150px;
      text-align: left;
    }

    &-operator {
      flex-grow: 1;
      text-align: right;
    }

    &-config {
      flex: 0 0 120px;
    }
  }

  :deep(.el-form-item__content) {
    height: 40px;
  }

  :deep(.el-form-item--medium .el-form-item__content) {
    height: 36px;
  }

  :deep(.el-form-item--small .el-form-item__content) {
    height: 32px;
  }

  :deep(.el-form-item--mini .el-form-item__content) {
    height: 28px;
  }

  :deep(.el-date-editor--datetimerange.el-input__inner) {
    width: 100% !important;
  }

  :deep(.el-date-editor--daterange.el-input__inner) {
    width: 100% !important;
  }

  :deep(.el-select) {
    width: 100% !important;
  }

}
</style>